﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using GraphAPI.Model;
using GraphEditor;

namespace GraphAPI
{
	public partial class GraphPanel : UserControl
	{
		public int vertexNameIndex = 1;
        public int relationNameIdex = 1;

		public GraphPanel()
		{
			InitializeComponent();

			graph = new Graph();
    	}

		public enum CursorState
		{
			Pointer,
			Node,
			Connection,
			OrientedConnection
		}

		List<Vertex> selectedVertexList = new List<Vertex>();

		private CursorState cursorState = CursorState.Pointer;

		private bool multiSelect = true;

		public bool MultiSelect
		{
			get { return multiSelect; }
			set { multiSelect = value; }
		}

		public CursorState State
		{
			get { return cursorState; }
			set
			{
				if (start != null)
					start.StrokeColor = Color.Black;
				if (end != null)
					end.StrokeColor = Color.Black;

				cursorState = value;

				SetNodesCursor(GetNodesCursor());
			}
		}

		private Cursor GetNodesCursor()
		{
			if (readOnly)
			{
				cursorState = CursorState.Pointer;
				return Cursors.Default;
			}

			if (cursorState == CursorState.Pointer)
				return Cursors.SizeAll;
			else
				return Cursors.Default;
		}

        private void SetNodesCursor(Cursor cur)
        {
            foreach (Vertex v in nodeList)
            {
                v.Cursor = cur;
            }
        }

        private Graph graph;

		public Graph Graph
		{
			get { return graph; }
			set { SetGraph(value); }
		}

		private bool readOnly = false;

		[Category("Behavior")]
		public bool ReadOnly
		{
			get { return readOnly; }
			set
			{
				readOnly = value;
				SetNodesCursor(GetNodesCursor());
			}
		}

        private void SetGraph(Graph graph)
		{
			Controls.Clear();
			nodeList.Clear();
			connectionList.Clear();
			selectedVertexList.Clear();
			selectedConnectionList.Clear();

			this.graph = graph;

			Vertex node;
			Dictionary<Entity, Vertex> eToV = new Dictionary<Entity,Vertex>();
			foreach (Entity ent in graph.Entitys)
			{
				node = AddVertex(ent);
				eToV.Add(ent, node);
			}

			//BaseConnection conn;
			foreach (Relation rel in graph.Relations)
			{
				AddRelation(rel, eToV[rel.StartEntity], eToV[rel.EndEntity]);
            }
		}


/**************************************************************************************************************************************************/
        //Вершина

		List<Connection> selectedConnectionList = new List<Connection>();

		private List<Vertex> nodeList = new List<Vertex>();
        
        //Добавляем новую вершину, добавляем к ней события
		private Vertex AddVertex(Entity ent)
		{
			Vertex node = new Vertex(ent);

			node.MouseDown += new MouseEventHandler(node_MouseDown);
			node.MouseMove += new MouseEventHandler(node_MouseMove);
			node.MouseUp += new MouseEventHandler(node_MouseUp);
			node.MouseClick += new MouseEventHandler(node_MouseClick);
			node.DoubleClick += new EventHandler(node_DoubleClick);
			node.DeleteVertex += new DeleteVertexHandler(node_DeleteVertex);
			node.ContextMenuStrip = nodeContextMenu;
			node.Cursor = GetNodesCursor();

			nodeList.Add(node);
			Controls.Add(node);
			return node;
		}
        
        protected override void OnMouseClick(MouseEventArgs e)
		{
			if (readOnly)
				return;

			if (e.Button != MouseButtons.Left)
				return;

			Entity entity;
			switch (cursorState)
			{
				case CursorState.Node:
					entity = new Entity(e.Location);
					entity.Name = (vertexNameIndex++).ToString();
					graph.AddEntity(entity);
					AddVertex(entity);
					break;

				case CursorState.Connection:
					break;

				case CursorState.OrientedConnection:
					break;

				case CursorState.Pointer:

					foreach (Vertex ver in selectedVertexList)
						ver.StrokeColor = Color.Black;

					selectedVertexList.Clear();

					foreach (Connection con in selectedConnectionList)
						con.ForeColor = Color.Black;

					selectedConnectionList.Clear();

					break;
			}

			base.OnMouseClick(e);
		}

        //Процедура удаления вершины
        void node_DeleteVertex(Vertex v)
		{
			graph.RemoveEntity(v.Entity);
			nodeList.Remove(v);
			this.Controls.Remove(v);
		}

		bool isDragging = false;
		Point movePoint;

        //Процедура для перемещения

		void node_MouseDown(object sender, MouseEventArgs e)
		{
			if (cursorState != CursorState.Pointer)
				return;

			if (cursorState != CursorState.Pointer)
				return;

			if (e.Button == MouseButtons.Left)
			{
                if (readOnly)
				    return;			    
				isDragging = true;
				movePoint = e.Location;

				Vertex v = sender as Vertex;
				if (frmNode.Visible || frmRel.Visible)
					AttributeNode(v);

				if (control)
				{
					if (selectedVertexList.Contains(v))
					{
						selectedVertexList.Remove(v);
						v.StrokeColor = Color.Black;
					}
					else
					{
						selectedVertexList.Add(v);
						v.StrokeColor = Color.Blue;
					}
				}
				else
				{
					foreach (Vertex ver in selectedVertexList)
					{
						ver.StrokeColor = Color.Black;
					}
					selectedVertexList.Clear();

					selectedVertexList.Add(v);
					v.StrokeColor = Color.Blue;
				}     
			}
		}

		void node_MouseMove(object sender, MouseEventArgs e)
		{
			if (readOnly)
				return;

			if (cursorState != CursorState.Pointer)
				return;

			if (e.Button == MouseButtons.Left)
			{
				if (isDragging)
				{
					;
					int dx = e.X - movePoint.X;
					int dy = e.Y - movePoint.Y;

					Vertex v = sender as Vertex;

/*                    if (dx < 0)
                        dx = 0 + v.Size.Width / 2;
                    if (dy < 0)
                        dy = 0 + v.Size.Height / 2;

                    if (dx > this.ClientSize.Width)
                        dx = this.ClientSize.Width - v.Size.Width / 2;
                    if (dy > this.ClientSize.Height)
                        dy = this.ClientSize.Height - v.Size.Height / 2;
*/
					v.Location = new Point(v.Left + dx, v.Top + dy);
                    //InvalidateEx();
				}
			}
		}

		void node_MouseUp(object sender, MouseEventArgs e)
		{
			if (readOnly)
				return;

			if (cursorState != CursorState.Pointer)
				return;

			if (e.Button == MouseButtons.Left)
			{
				isDragging = false;

			}
		}

        //Процедура для изменениии метки вершины
		void node_DoubleClick(object sender, EventArgs e)
		{
			if (readOnly)
				return;

			if (cursorState != CursorState.Pointer)
				return;
			Vertex v = sender as Vertex;
			AttributeNode(v);
		}

       Vertex contextNode = null;

		private void alignToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (contextNode == null)
				return;

			ToolStripMenuItem item = sender as ToolStripMenuItem;

			string tag = item.Tag as string;
			if (tag == null)
				return;

			TextAlign align;
			try
			{
				align = (TextAlign)Enum.Parse(typeof(TextAlign), tag);
			}
			catch
			{
				return;
			}

			contextNode.TextAlign = align;
		}



		private void nodeContextMenu_Opening(object sender, CancelEventArgs e)
		{
			if (readOnly)
			{
				e.Cancel = true;
				return;
			}

			if (cursorState != CursorState.Pointer)
			{
				e.Cancel = true;
				return;
			}

			ContextMenuStrip cm1 = sender as ContextMenuStrip;

			if (cm1 == null)
				return;

			contextNode = cm1.SourceControl as Vertex;
			if (contextNode == null)
				return;

			centrToolStripMenuItem.Checked = contextNode.TextAlign == TextAlign.Center;
			topToolStripMenuItem.Checked = contextNode.TextAlign == TextAlign.Top;
			rightToolStripMenuItem.Checked = contextNode.TextAlign == TextAlign.Right;
			leftToolStripMenuItem.Checked = contextNode.TextAlign == TextAlign.Left;
			bottomToolStripMenuItem.Checked = contextNode.TextAlign == TextAlign.Bottom;
			noneToolStripMenuItem.Checked = contextNode.TextAlign == TextAlign.None;
		}

		private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (contextNode == null)
				return;

			contextNode.Delete();
		}

/***************************************************************************************************************************************************/
        //Связь

        private List<Connection> connectionList = new List<Connection>();

        Vertex start;
		Vertex end;

        Connection contextRelation = null;

		private Connection AddRelation(Relation rel, Vertex start, Vertex end)
		{
			Connection connection = new Connection(start, end, rel);

			connection.DeleteConnection += new DeleteConnectionHandler(connection_DeleteConnection);
			connection.DoubleClick += new EventHandler(conn_DoubleClick);
			connection.KeyDown += new KeyEventHandler(conn_KeyDown);
			connection.MouseDown += new MouseEventHandler(conn_MouseDown);
			connection.ContextMenuStrip = relationContextMenu;

			connectionList.Add(connection);
			Controls.Add(connection);

			return connection;
		}

		private void conn_MouseDown(object sender, MouseEventArgs e)
		{
			if (readOnly)
				return;

			Connection con = sender as Connection;
			if (frmNode.Visible || frmRel.Visible)
				AttributeRel(con);
		}

        //Процедура кликов по вершинам, для создания связей между ними
		private void node_MouseClick(object sender, MouseEventArgs e)
		{
			if (readOnly)
				return;

            if (e.Button != MouseButtons.Left)
                return;

			if (cursorState == CursorState.Connection || cursorState == CursorState.OrientedConnection)
			{
				if (start == null)
				{
					start = sender as Vertex;
					start.StrokeColor = Color.LightGreen;
				}
				else
				{
					Vertex v = sender as Vertex;
					if (start != v)
						end = v;
				}

				if (start != null && end != null)
				{
					int intStart = graph.GetEntityId(start.Entity);
					int intEnd = graph.GetEntityId(end.Entity);

					if (graph.RelationMatrix[intStart, intEnd])
						return;

					Relation relation = new Relation(
						start.Entity,
						end.Entity,
						cursorState == CursorState.OrientedConnection,
						relationNameIdex.ToString());

                    relationNameIdex++;

					graph.AddRelation(relation);

					this.AddRelation(relation, start, end);

					start.StrokeColor = Color.Black;
					end.StrokeColor = Color.Black;
					start = null;
					end = null;
				}
			}
		}

        private void conn_DoubleClick(object sender, EventArgs e)
        {
            if (readOnly)
                return;

            if (cursorState != CursorState.Pointer)
                return;

            Connection r = sender as Connection;

			AttributeRel(r);

        }

		void connection_DeleteConnection(Connection c)
		{
			graph.RemoveRelation(c.Relation);
			connectionList.Remove(c);
			this.Controls.Remove(c);
		}

        

        private void relationContextMenu_Opening(object sender, CancelEventArgs e)
        {
            if (readOnly)
            {
                e.Cancel = true;
                return;
            }

            if (cursorState != CursorState.Pointer)
            {
                e.Cancel = true;
                return;
            }

            ContextMenuStrip cm2 = sender as ContextMenuStrip;

            if (cm2 == null)
                return;

            contextRelation = cm2.SourceControl as Connection;

            if (contextRelation == null)
                return;
        }

		private void conn_KeyDown(object sender, KeyEventArgs e)
		{
				Connection c = sender as Connection;
				if (control)
				{
					foreach (Connection con in selectedConnectionList)
						if (con == c)
						{
							selectedConnectionList.Remove(con);
							con.ForeColor = Color.Black;
							return;
						}
					selectedConnectionList.Add(c);
					c.ForeColor = Color.Blue;
				}
				else
				{
					for (int i = 0; i < selectedConnectionList.Count; i++)
					{
						Connection con = selectedConnectionList[i];
						con.ForeColor = Color.Black;
					}
					selectedConnectionList.Clear();

					selectedConnectionList.Add(c);
					c.ForeColor = Color.Blue;
				}

		}

        private void удалитьСвязьToolStripMenuItem_Click(object sender, EventArgs e)
        {
            connection_DeleteConnection(contextRelation);
        }

        private void editCaptionToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

		bool control = false;


		Point pMin;
		Point pMax;
		private void SelectRegion(Point pointBegin, Point pointEnd)
		{
			if (pointBegin.X < pointEnd.X)
			{
				pMin.X = pointBegin.X;
				pMax.X = pointEnd.X;
			}
			else
			{
				pMin.X = pointEnd.X;
				pMax.X = pointBegin.X;
			}

			if (pointBegin.Y < pointEnd.Y)
			{
				pMin.Y = pointBegin.Y;
				pMax.Y = pointEnd.Y;
			}
			else
			{
				pMin.Y = pointEnd.Y;
				pMax.Y = pointBegin.Y;
			}

			foreach(Vertex vert in nodeList)
				if (vert.Center.X > pMin.X && vert.Center.X < pMax.X && vert.Center.Y > pMin.Y && vert.Center.Y < pMax.Y)
				{
					vert.StrokeColor = Color.Blue;
					selectedVertexList.Add(vert);
				}
		}

		private void GraphPanel_KeyDown(object sender, KeyEventArgs e)
		{
			control = e.Control;

			if (e.KeyCode == Keys.Delete)
			{
				foreach (Vertex v in selectedVertexList)
				{
					v.Delete();
				}

				selectedVertexList.Clear();
			}

		}

		private void GraphPanel_KeyUp(object sender, KeyEventArgs e)
		{
			control = e.Control;
		}

		Point pointBegin;
		private void GraphPanel_MouseDown(object sender, MouseEventArgs e)
		{
			if (cursorState == CursorState.Pointer)
			{
				pointBegin.X = e.X;
				pointBegin.Y = e.Y;
			}
		}

		Point p;
		private void GraphPanel_MouseUp(object sender, MouseEventArgs e)
		{
			if (readOnly)
				return;

			if (cursorState == CursorState.Pointer)
			{
				p.X = e.X;
				p.Y = e.Y;

				SelectRegion(pointBegin, p);
			}
		}

		private void GraphPanel_KeyPress(object sender, KeyPressEventArgs e)
		{
			
		}

		private void свойстваToolStripMenuItem1_Click(object sender, EventArgs e)
		{
			AttributeNode(contextNode);
		}

		private void свойстваToolStripMenuItem_Click(object sender, EventArgs e)
		{
			AttributeRel(contextRelation);
		}

		FormPropertiesNode frmNode = new FormPropertiesNode();
		FormPropertiesRelation frmRel = new FormPropertiesRelation();
		
        private void AttributeNode(Vertex ver)
        {
			if (frmNode.Visible)
			{
				frmNode.SetNode(ver);
				return;
			}

			if (frmRel.Visible)
			{
				frmRel.Hide();
				frmNode.SetNode(ver);

				frmNode.Left = frmRel.Left;
				frmNode.Top = frmRel.Top;

				frmNode.Show(this);
				return;
			}

			frmNode.SetNode(ver);
			frmNode.Show(this);
        }

		private void AttributeRel(Connection con)
		{
			if (frmRel.Visible)
			{
				frmRel.SetRelation(con, graph);
				return;
			}

			if (frmNode.Visible)
			{
				frmNode.Hide();
				frmRel.SetRelation(con, graph);

				frmRel.Left = frmNode.Left;
				frmRel.Top = frmNode.Top;

				frmRel.Show(this);
				return;
			}

			frmRel.SetRelation(con, graph);
			frmRel.Show(this);
		}

	}
}
